﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FeatureModelCore;
using Collections.TreeCollections;
using System.Windows;
using System.Windows.Media;
using FeatureModelCore.FMShapes;
using FeatureModelTool.FMAlgorithms;
using FeatureModelTool.FMAlgorithms.FMGraphicAlgorithms;
using System.Windows.Shapes;
using FeatureModelCore.FMTypes;
using FeatureModelCore.Layout;
using FeatureModelCore.FMCrossTreeConstraint;
using FeatureModelCore.FMInterface;

namespace FeatureModelTool.FeatureModelGraphical.GraphicalControls
{
    public class Control_Constraint : IControl
    {
        public readonly static double ARROW_R = Math.PI / 12;
        protected readonly static double _SLICE_LENGTH = TreeLayout.BLOCK_SIZE / 4;

        public CrossTreeConstraint FeatureInstance { get { return _cons; } }
        public FeatureModelGraphicalView Canvas { get { return _canvas; } }
        public Brush Background { get { return Brushes.Black; } }
        public int BorderThickness
        {
            get
            {
                if (_isFocus)
                    return FeatureModelGraphicalView.FOCUS_THICKNESS;
                return FeatureModelGraphicalView.DEFAULT_THICKNESS;
            }
        }
        public bool IsFocus { get { return _isFocus; } }

        #region Fields
        /// <summary>
        /// Constraint
        /// </summary>
        private CrossTreeConstraint _cons;
        private FeatureModelGraphicalView _canvas;
        private bool _isFocus;

        private Path _path;
        //private 
        #endregion

        #region Constructor
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="cons"></param>
        public Control_Constraint(FeatureModelGraphicalView canvas, CrossTreeConstraint cons)
        {
            _cons = cons;
            _canvas = canvas;
            _isFocus = false;

            _path = new Path();
            _path.StrokeThickness = BorderThickness;
            _path.Stroke = Brushes.Black; 
            RefreshPosition();
        }
        #endregion

        #region Focus
        /// <summary>
        /// Set Focus
        /// </summary>
        public void Focus()
        {
            this._canvas.ClearFocus();
            this._isFocus = true;

            _canvas.Refresh(this, null);
        }
        /// <summary>
        /// Clear Focus
        /// </summary>
        public void ClearFocus()
        {
            _isFocus = false;
        }
        #endregion

        /// <summary>
        /// To UI Element
        /// </summary>
        /// <returns></returns>
        public UIElement[] ToUIElement()
        {
            return new UIElement[] { _path };
        }

        public void SetPosition(FMPoint point, bool toRefresh)
        {
            throw new NotImplementedException();
        }

        public void SetPosition(double x, double y, bool toRefresh)
        {
            throw new NotImplementedException();
        }

        public void RefreshPosition()
        {
            try
            {
                // rectangle 
                FMRectangle rectA = _cons.NodeA.Shape;
                FMRectangle rectB = _cons.NodeB.Shape;

                List<FMRectangle> rectList = new List<FMRectangle>();
                List<FMCircle> circleList = new List<FMCircle>();
                // get all rect
                FeatureModelTree fmTree = (FeatureModelTree)_cons.Tree;
                foreach (IFeature node in fmTree.AllFeatureNodes)
                {
                    rectList.Add(node.Shape);
                }
                foreach (FeatureTreeGroupRelationship gR in fmTree.AllGroupRelationships)
                {
                    circleList.Add(gR.Shape);
                }

                double panelWidth = _canvas.ActualWidth, panelHeight = _canvas.ActualHeight;
                FMPoint[] linePath = ConstraintGraphicalAlgorithm.GetLinePath(rectA, rectB, rectList.ToArray<FMRectangle>()
                    , circleList.ToArray<FMCircle>(), panelWidth, panelHeight, TreeLayout.BLOCK_SIZE);

                // set dotted line
                PathFigure[] lineFirgures = drawDottedLines(linePath);
                PathGeometry _lineGeometry = new PathGeometry();
                foreach (PathFigure pf in lineFirgures)
                {
                    _lineGeometry.Figures.Add(pf);
                }

                // set angle
                PathFigure[] arrowFigures = drawArrow(linePath[linePath.Length - 1]
                    , linePath[linePath.Length - 2]);
                _lineGeometry.Figures.Add(arrowFigures[0]);
                _lineGeometry.Figures.Add(arrowFigures[1]);
                if (this.FeatureInstance.Type == FMCrossTreeConstraintType.Exclude)
                {
                    // double arrow
                    arrowFigures = drawArrow(linePath[0]
                , linePath[1]);
                    _lineGeometry.Figures.Add(arrowFigures[0]);
                    _lineGeometry.Figures.Add(arrowFigures[1]);
                }

                _path.Data = _lineGeometry;
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="center"></param>
        /// <returns></returns>
        protected static PathFigure[] drawArrow(FMPoint center, FMPoint directionPoint)
        {
            double r = GraphicAngleAlgorithms.CalculateAngle(directionPoint, center);
            double uR = r - ARROW_R;
            double dR = r + ARROW_R;
            GraphicAngleAlgorithms.ValidRadian(ref uR);
            GraphicAngleAlgorithms.ValidRadian(ref dR);

            PathFigure[] pfs = new PathFigure[2];
            pfs[0] = drawArrowEdge(center, uR);
            pfs[1] = drawArrowEdge(center, dR);
            return pfs;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="center"></param>
        /// <param name="r"></param>
        /// <returns></returns>
        private static PathFigure drawArrowEdge(FMPoint center, double r)
        {
            FMPoint point = GraphicAngleAlgorithms.CalculatePoint(center, r, _SLICE_LENGTH * 2);
            LineSegment line = new LineSegment(new Point(point.X, point.Y), true);
            PathFigure arrowFirgure = new PathFigure();
            arrowFirgure.StartPoint = new Point(center.X, center.Y);
            arrowFirgure.Segments.Add(line);
            return arrowFirgure;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="points"></param>
        /// <returns></returns>
        private static PathFigure[] drawDottedLines(FMPoint[] points)
        {
            if (points.Length <= 1)
            {
                return null;
            }
            List<PathFigure> ret = new List<PathFigure>();

            for (int i = 1; i < points.Length; ++i)
            {
                ret.AddRange(drawDottedLine(points[i - 1], points[i]));
            }
            return ret.ToArray<PathFigure>();
        }
        /// <summary>
        /// Draw dotted line
        /// </summary>
        /// <param name="startPoint"></param>
        /// <param name="endPoint"></param>
        /// <returns></returns>
        protected static PathFigure[] drawDottedLine(FMPoint startPoint
            , FMPoint endPoint)
        {
            // calculate the distance between start point and end point
            double dis = GraphicAngleAlgorithms.CalculationDestination(startPoint, endPoint);
            // no use to draw
            if (dis == 0)
            {
                return null;
            }
            // calculate how many slices
            int slices = (int)(dis / (_SLICE_LENGTH * 2));

            if (slices == 0)
            { return new PathFigure[slices];}

            // draw dotted line
            PathFigure[] lineFirgures = new PathFigure[slices];
            int i = 0;
            for (i = 0; i < slices - 1; ++i)
            {
                FMPoint sliceEndPoint = GraphicAngleAlgorithms.CalculatePointOnline
                    (startPoint, endPoint, _SLICE_LENGTH);
                startPoint = GraphicAngleAlgorithms.CalculatePointOnline
                    (startPoint, endPoint, _SLICE_LENGTH * 2);
                lineFirgures[i] = drawLineSegment(startPoint, sliceEndPoint);
            }
            // for last slice
            lineFirgures[i] = drawLineSegment(startPoint, endPoint);

            return lineFirgures;
        }

        /// <summary>
        /// Draw one segment of dotted line
        /// </summary>
        /// <param name="startPoint"></param>
        /// <param name="endPoint"></param>
        /// <returns></returns>
        private static PathFigure drawLineSegment(FMPoint startPoint
            , FMPoint endPoint)
        {
            LineSegment segment = new LineSegment(new Point(endPoint.X
                , endPoint.Y), true);
            PathFigure pf = new PathFigure();
            pf.StartPoint = new Point(startPoint.X, startPoint.Y);
            pf.Segments.Add(segment);

            return pf;
        }
    }
}
